package route

import (
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"net/http/pprof"
	"os"
	"strconv"
	"time"
)

type tokenStruct struct {
	redis          string
	header         string
	frontendExpire time.Duration
	tokenExpire    int
}

var (
	// 主协议
	Protocol = "https://"

	// scrypt 加密序列
	ScryptSlice = [4]int{16384, 8, 1, 64}

	// 公共 time 格式
	TimeFormat = time.UnixDate

	// token 参数
	dynamic = tokenStruct{
		redis:          "admin_certfication_dynamic:",
		header:         "X-JWT-Authorization-Dynamic",
		frontendExpire: time.Minute * 10, // 记录在 token / 10 minutes -- 动态 token 过期时间默认 10 分钟
		tokenExpire:    2 * 60 * 60,      // 记录在 redis / 2 hours -- 比前端的 expire 长, 目的是做到 fresh token
	}
	static = tokenStruct{
		redis:          "admin_certfication_static:",
		header:         "X-JWT-Authorization-Static",
		frontendExpire: time.Hour * 8, // 记录在 token / 8 hours -- 静态 token 过期时间默认 8 hours
		tokenExpire:    8 * 60 * 60,   // 记录在 redis / 8 hours
	}

	// 一般路由的配置规则
	// 方便记录操作的 路由和路由描述
	routeMap = Map{
		m: make(map[string]*Rule),
	}

	// admin_log 限制
	// 记录最大值是 MaxLog, 但是每次删除记录只会留下 DisplayLog 条
	// 目的是避免超过限制后, 频繁地进行 删除记录 操作
	DisplayLog = 50  // 展示/删除操作后剩余的记录数
	MaxLog     = 100 // 数据库(mariadb)中记录的最大数

	// Browser 静态文件缓存策略
	browserFile = struct {
		timeFormat   string
		expire       time.Duration
		maxAge       string
		lastModified time.Duration // Last-Modified // Last-Modified-Since
		etag         time.Duration // Etag // If-None-Match
	}{
		timeFormat: time.RFC1123,
		expire:     time.Hour * 24 * 7, // 1 week
		maxAge:     strconv.FormatInt(int64(time.Hour*24*7), 10),
	}

	// Normal Token
	// 避免重复提交的 Token
	NormalTokenSETExpire = 10 * 60 // 10 mins

	ModeDir  = os.ModeDir | 0755
	ModeFile = 0644

	FileUploadTokenHeader        = "X-Token-FileUpload"
	FileUploadDir                = "/tmp/demonstration"
	FileUploadDirTmp             = FileUploadDir + string(os.PathSeparator) + "tmp"
	FileUploadChunk       uint64 = 8192 // 8k
	FileDownloadHeader           = "X-FileDownload"

	// End Normal Token

	// NOTE: 这种做法会导致单件, 和 NewRouter 的初衷不一致
	// CustomStmts *StmtMap
)

// NewRouter 一般路由
func NewRouter() (http.Handler, <-chan func() *http.Request, func()) {
	r := mux.NewRouter()
	// 调试路由
	r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	r.HandleFunc("/debug/pprof/profile", pprof.Profile)
	r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	r.HandleFunc("/debug/pprof/trace", pprof.Trace)
	r.PathPrefix("/debug/pprof").HandlerFunc(pprof.Index)

	// 静态文件处理
	// e.g.
	r.PathPrefix("/config").Handler(http.FileServer(http.Dir("/config"))).
		Headers(
			"Expire", time.Now().Add(browserFile.expire).Format(browserFile.timeFormat),
			"Cache-Control", "max-age="+browserFile.maxAge,
		)

	// 404 路由
	r.NotFoundHandler = http.HandlerFunc(NotFoundHandler)

	// 更新 tls 证书
	ch := make(chan func() *http.Request)
	uc := &UpdateCert{
		Ch: ch,
	}

	// 业务路由
	r.PathPrefix("/api/v1").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 跨域
		w.Header().Set("access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type") // 跨域头
	}).Methods("OPTIONS")

	rv1 := r.PathPrefix("/api/v1").Subrouter()

	rv1.Path("/update-cert").Handler(uc)
	// 登陆/注册子路由
	auth := rv1.PathPrefix("/auth").Subrouter()
	auth.HandleFunc("/loginin/jwt", AdminLoginInByJWT).Methods("POST")
	auth.HandleFunc("/loginin/form", AdminLoginInByForm).Methods("POST")
	auth.HandleFunc("/loginup", AdminLoginUp).Methods("POST")

	// 后台
	back := rv1.PathPrefix("/backstage").Subrouter()
	// 所有数据请求都需要过验证(/loginin/** 和 /loginup 独自验证)
	back.Use(MdlCheckDynamicToken) // 动态 token

	// 可能不好用 ?
	// rootRouter.Router = back
	// routeMap.RLock()
	// for key, value := range routeMap.m {
	// 	rootRouter.RouteRange(key, value)
	// }
	// routeMap.RUnlock()

	back.HandleFunc("/pre-file", PreFileUpload).Methods("GET")
	back.HandleFunc("/file", FileList).Methods("GET")

	file := back.PathPrefix("/file").Subrouter() // 文件处理路由
	file.Handle("/{file}", MdlCheckStaticToken(http.HandlerFunc(FileUpload))).Methods("POST")
	file.HandleFunc("/{file}", FileDownload).Methods("GET")

	return r, ch, uc.close
}

// NotFoundHandler 404 则重映射到主页
func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
	// fmt.Println("redirect")
	// fmt.Println(r.URL.Scheme, r.URL, r.Host)
	w.Header().Set("Connection", "close")
	tourl := Protocol + r.Host + "/page/home" // 重映射到登陆/注册[页面]
	http.Redirect(w, r, tourl, http.StatusMovedPermanently)
}

// test -- 测试路由
func testRoute(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("ok"))
	w.WriteHeader(http.StatusOK)
}

// UpdateCert 用于 更新证书 等只运行一次
type UpdateCert struct {
	Ch chan<- func() *http.Request
}

func (uc *UpdateCert) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()

	uc.Ch <- func() *http.Request {
		return r
	}
}

func (uc *UpdateCert) close() {
	close(uc.Ch)
}
